package cn.easyplatform.studio.dao.impl;

import cn.easyplatform.entities.beans.ResourceBean;
import cn.easyplatform.entities.beans.project.ProjectBean;
import cn.easyplatform.entities.transform.TransformerFactory;
import cn.easyplatform.lang.Files;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.studio.dao.DaoException;
import cn.easyplatform.studio.dao.DaoUtils;
import cn.easyplatform.studio.dao.Dialect;
import cn.easyplatform.studio.dao.ModuleDao;
import cn.easyplatform.studio.dao.transaction.JdbcTransactions;
import cn.easyplatform.studio.vos.ModuleVo;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.utils.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.lang.Strings;

import javax.sql.DataSource;
import java.io.File;
import java.io.Serializable;
import java.sql.*;
import java.util.*;

public abstract class AbstractModuleDao implements ModuleDao {

    protected final static Logger log = LoggerFactory.getLogger(AbstractModuleDao.class);

    private DataSource ds;

    public AbstractModuleDao(DataSource dataSource) {
        this.ds = dataSource;
    }

    protected abstract Dialect getDialect();
    @Override
    public List<ModuleVo> getModule(String table, String searchStr){
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Connection conn = JdbcTransactions.getConnection(ds);
            StringBuilder sb = new StringBuilder();
            sb.append("SELECT moduleId,moduleName,price,desp,pics,entityIds,chooseType,rootEntityIds,dicDetailID,createDate," +
                    "updateDate,createUser,updateUser FROM " + table + " WHERE 1=1");
            if (Strings.isBlank(searchStr) == false) {
                sb.append(" and (moduleId like '%" + searchStr + "%' or moduleName like '%" + searchStr +
                        "%' or desp " + "like '%" + searchStr + "%' or entityIds like '%" + searchStr + "%')");
            }
            pstmt = conn.prepareStatement(sb.toString());
            rs = pstmt.executeQuery();
            List<ModuleVo> vos = new ArrayList<>();
            while (rs.next()) {
                ModuleVo vo = new ModuleVo(rs.getString(1), rs.getString(2),
                        rs.getString(3), rs.getString(4), rs.getString(5),
                        rs.getString(6), rs.getString(7), rs.getString(8),
                        rs.getString(9), rs.getTimestamp(10), rs.getTimestamp(11),
                        rs.getString(12), rs.getString(13));
                vos.add(vo);
            }
            return vos;
        } catch (SQLException ex) {
            throw new DaoException(ex.getMessage());
        } finally {
            DaoUtils.closeQuietly(pstmt, rs);
        }
    }

    @Override
    public boolean addModule(String table, ModuleVo moduleVo){
        if (moduleVo == null)
        {
            return false;
        }
        synchronized (moduleVo){
            PreparedStatement pstmt = null;
            Connection conn = null;
            try {
                conn = JdbcTransactions.getConnection(ds);
                pstmt = conn
                        .prepareStatement("insert into " + table +
                                " (moduleId,moduleName,price,desp,pics,entityIds,chooseType,rootEntityIds,dicDetailID,createUser) " +
                                "values(?,?,?,?,?,?,?,?,?,?)");
                pstmt.setString(1, moduleVo.getModuleId());
                pstmt.setString(2, moduleVo.getModuleName());
                pstmt.setString(3, moduleVo.getPrice());
                pstmt.setString(4, moduleVo.getDesp());
                pstmt.setString(5, moduleVo.getPics());
                pstmt.setString(6, moduleVo.getEntityIds());
                pstmt.setString(7, moduleVo.getChooseType());
                pstmt.setString(8, moduleVo.getRootEntityIds());
                pstmt.setString(9, moduleVo.getDicDetailID());
                pstmt.setString(10, moduleVo.getCreateUser());
                pstmt.executeUpdate();
            } catch (SQLException ex) {
                throw new DaoException(ex.getMessage());
            } finally {
                DaoUtils.closeQuietly(pstmt);
            }
            return true;
        }
    }

    @Override
    public boolean updateModule(String table, ModuleVo moduleVo){
        if (moduleVo == null)
        {
            return false;
        }
        synchronized (moduleVo){
            PreparedStatement pstmt = null;
            Connection conn = null;
            try {
                conn = JdbcTransactions.getConnection(ds);
                pstmt = conn.prepareStatement("UPDATE " + table +
                        " SET moduleName=?,price=?,desp=?,pics=?,entityIds=?,chooseType=?,rootEntityIds=?,dicDetailID=?" +
                        ",updateUser=?,updateDate=? WHERE moduleId=?");
                pstmt.setString(1, moduleVo.getModuleName());
                pstmt.setString(2, moduleVo.getPrice());
                pstmt.setString(3, moduleVo.getDesp());
                pstmt.setString(4, moduleVo.getPics());
                pstmt.setString(5, moduleVo.getEntityIds());
                pstmt.setString(6, moduleVo.getChooseType());
                pstmt.setString(7, moduleVo.getRootEntityIds());
                pstmt.setString(8, moduleVo.getDicDetailID());
                pstmt.setString(9, moduleVo.getUpdateUser());
                pstmt.setTimestamp(10, new Timestamp(System.currentTimeMillis()));
                pstmt.setString(11, moduleVo.getModuleId());
                pstmt.execute();
            } catch (SQLException ex) {
                throw new DaoException(ex.getMessage());
            } finally {
                DaoUtils.closeQuietly(pstmt);
            }
        }
        return true;
    }

    @Override
    public boolean deleteModule(String table, ModuleVo moduleVo){
        if (moduleVo == null)
        {
            return false;
        }
        PreparedStatement pstmt = null;
        Connection conn = null;
        try {
            conn = JdbcTransactions.getConnection(ds);
            pstmt = conn
                    .prepareStatement("delete from " + table +
                            " where 1 = 1 and moduleId = ?");
            pstmt.setString(1, moduleVo.getModuleId());
            pstmt.executeUpdate();
        } catch (SQLException ex) {
            throw new DaoException(ex.getMessage());
        } finally {
            DaoUtils.closeQuietly(pstmt);
        }
        return true;
    }

    @Override
    public void backup(String dir, String templateId, String... modules) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            conn = ds.getConnection();
            Serializable[] data = new Serializable[3];
            //ep_module_info记录
            //获取源基目信息
            pstmt = conn.prepareStatement("select content from ep_model_info where modelId=? AND type=? AND status<>'D'");
            pstmt.setString(1, templateId);
            pstmt.setString(2, EntityType.PROJECT.getName());
            rs = pstmt.executeQuery();
            ProjectBean project = null;
            ;
            if (rs.next())
                project = TransformerFactory.newInstance().transformFromXml(ProjectBean.class, Lang.ins(rs.getString(1)));
            else
                throw new DaoException("app.source.not.found", new Throwable(templateId));
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);
            //数据源
            pstmt = conn.prepareStatement("select content from ep_model_info where modelId=? AND type=? AND status<>'D'");
            pstmt.setString(1, project.getBizDb());
            pstmt.setString(2, EntityType.DATASOURCE.getName());
            rs = pstmt.executeQuery();
            ResourceBean datasource = null;
            if (rs.next())
                datasource = TransformerFactory.newInstance().transformFromXml(ResourceBean.class, Lang.ins(rs.getString(1)));
            else
                throw new DaoException("app.source.not.found",  new Throwable(project.getBizDb()));
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);
            //版本号
            pstmt = conn.prepareStatement("select keyvalue from ep_serial_info where keyname=?");
            pstmt.setString(1, templateId);
            rs = pstmt.executeQuery();
            Long maxVersionNo = null;
            if (rs.next())
                maxVersionNo = rs.getLong(1);
            else
                throw new DaoException("app.source.not.found", new Throwable(templateId));
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);

            //保存
            project.setLicense(null);
            data[0] = project;
            data[1] = datasource;
            data[2] = maxVersionNo;
            if (log.isInfoEnabled())
                log.info(String.format("backup %s %s %s", templateId, project.getBizDb(), maxVersionNo + ""));
            byte[] content = SerializationUtils.serialize(data);
            File file = new File(dir + "/" + templateId + "/entry.info");
            Files.write(file, content);
            data = null;
            content = null;
            if (modules.length == 0) {//备份全部
                data = new Serializable[5];
                pstmt = conn.prepareStatement("select * from ep_" + templateId);
                data[0] = getRows(pstmt);
                DaoUtils.closeQuietly(pstmt);
                pstmt = conn.prepareStatement("select * from ep_" + templateId + "_repo");
                data[1] = getRows(pstmt);
                DaoUtils.closeQuietly(pstmt);
                pstmt = conn.prepareStatement("select * from ep_" + templateId + "_link");
                data[2] = getRows(pstmt);
                DaoUtils.closeQuietly(pstmt);
                pstmt = conn.prepareStatement("select * from ep_" + templateId + "_task_link");
                data[3] = getRows(pstmt);
                DaoUtils.closeQuietly(pstmt);
                pstmt = conn.prepareStatement("select * from ep_" + templateId + "_module");
                data[4] = getRows(pstmt);
                DaoUtils.closeQuietly(pstmt);
                content = SerializationUtils.serialize(data);
                file = new File(dir + "/" + templateId + "/entry.dat");
                Files.write(file, content);
                data = null;
                content = null;
            } else {//其它按目录保存

            }
            DaoUtils.closeQuietly(conn);

            //复制业务数据库
            conn = DriverManager.getConnection(datasource.getProps().get("jdbcUrl"), datasource.getProps().get("username"), datasource.getProps().get("password"));
            DatabaseMetaData dbmd = conn.getMetaData();
            rs = dbmd.getTables(null, null, null, new String[]{"TABLE",
                    "VIEW"});
            Statement stmt = conn.createStatement();
            while (rs.next()) {
                String table = rs.getString("TABLE_NAME");
                ResultSet set = stmt.executeQuery(String.format("SHOW CREATE TABLE %s", table));
                if (set.next()) {
                    String createSQL = set.getString(2);
                    DaoUtils.closeQuietly(set);
                    if (log.isInfoEnabled())
                        log.info(createSQL);
                    pstmt = conn.prepareStatement("select * from " + table);
                    ArrayList<Object> rows = getRows(pstmt);
                    DaoUtils.closeQuietly(pstmt);
                    Map<String, String> info = new HashMap<>();
                    info.put("table", table);
                    info.put("sql", createSQL);
                    rows.add(0, info);
                    content = SerializationUtils.serialize(rows);
                    file = new File(dir + "/" + project.getBizDb() + "/" + table + ".dat");
                    Files.write(file, content);
                    data = null;
                    content = null;
                }
            }
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(stmt);
        } catch (SQLException e) {
            throw new DaoException("dao.access.backupProject", e);
        } finally {
            DaoUtils.closeQuietly(rs);
            DaoUtils.closeQuietly(pstmt);
            DaoUtils.closeQuietly(conn);
        }
    }

    private ArrayList<Object> getRows(PreparedStatement pstmt) throws SQLException {
        ResultSet rs = pstmt.executeQuery();
        ResultSetMetaData rsmd = rs.getMetaData();
        int count = rsmd.getColumnCount();
        ArrayList<Object> rows = new ArrayList<>();
        while (rs.next()) {
            Map<String, Object> row = new LinkedHashMap<>();
            for (int i = 1; i <= count; i++)
                row.put(rsmd.getColumnName(i), rs.getObject(i));
            rows.add(row);
        }
        rs.close();
        return rows;
    }
}
